#include <stdio.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#define TPORT 40001
#define TBACKLOG 20
#define MAXEVENT 20
#define MAXLINE 4096
#include "Person.h"

int main()
{
    // the epoll created fd
    int epfd = 0;
    printf("port:%d\n", TPORT);

    // the listening fd
    int listenfd = 0;
    int connfd = 0;
    int sockfd = 0;

    // the event
    struct epoll_event ev;
    struct epoll_event events[MAXEVENT];

    // the addr of server
    struct sockaddr_in srvAddr;
    struct sockaddr_in cliAddr;
    socklen_t clilen;

    int nfds = 0;

    int i = 0;
    int n = 0;

    char BUF[MAXLINE] = {0};

    //1. create epoll
    epfd = epoll_create(/*since linux 2.6.8, the size argument is ignored, but must be greater than zero*/1);

    //2. create listening fd
    listenfd = socket(AF_INET, SOCK_STREAM, /*if this argument is zero, will use the default value by argument 2*/0);

    //3. 指定事件所从属的目标文件描述符
    ev.data.fd = listenfd;
    ev.events = EPOLLIN | EPOLLET;

    //4. 注册epoll事件
    //4.1: epfd 是 epoll_create() 的返回值
    //4.2: 第二个参数表示动作
    //4.2.1: EPOLL_CTL_ADD 注册新的fd到epfd中
    //4.2.2: EPOLL_CTL_MOD 修改已经注册的fd的事件
    //4.2.3: EPOLL_CTL_DEL 从epfd中删除一个fd
    //4.3: 第三个参数是需要监听的fd
    //4.4: 告诉内核需要监听什么事
    epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);

    //5. bind
    bzero(&srvAddr, sizeof(srvAddr));
    srvAddr.sin_family = AF_INET;
    srvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    srvAddr.sin_port = htons(TPORT);
    bind(listenfd, (struct sockaddr *)&srvAddr, sizeof(srvAddr));

    //6.listen
    listen(listenfd, TBACKLOG);

    clilen = sizeof(cliAddr);

    while(1)
    {
        nfds = epoll_wait(epfd, events, MAXEVENT, 0);
        for(i = 0; i < nfds; i++)
        {
            if(events[i].data.fd == listenfd)
            {
                // 如果是新监测到连接活动
                connfd = accept(listenfd, (struct sockaddr *)&cliAddr, &clilen);
                char * str = inet_ntoa(cliAddr.sin_addr);
                printf("accept from client %s\n", str);
                ev.data.fd = connfd;
                ev.events = EPOLLIN | EPOLLET;
                epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
            }
            else if(events[i].events & EPOLLIN)
            {
                // 如果是已连接的
                // 进行读入
                printf("read...\n");
                if((sockfd = events[i].data.fd)<0)
                {
                    continue;
                }
		// 下一行即将read
                if((n = read(sockfd, BUF, MAXLINE)) < 0)
                {
                    if(errno == ECONNRESET)
                    {
                        close(sockfd);
                        events[i].data.fd = -1;
                    }
                    else
                    {
                        printf("readline err!\n");
                    }
                }
                else if(n == 0)
                {
                    close(sockfd);
                    events[i].data.fd = -1;
                }
		// 接收到了数据
		Person p;
		memcpy(&p, BUF, sizeof(Person));

		// 打印出来
		printf("p.m_Age = %d\n", p.m_Age);
		// 处理之
		p.m_Age += 1;
		memcpy(BUF, &p, sizeof(Person));

                //BUF[n] = '\0';
                printf("after epollin\n");
                ev.data.fd = sockfd;
                ev.events = EPOLLOUT | EPOLLET;
                // 读完了准备写
                epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
            }
            else if(events[i].events & EPOLLOUT)
            {
                sockfd = events[i].data.fd;

		// 向socket写入数据
		printf("下一行向socket写入数据\n");
                write(sockfd, BUF, n);
                ev.data.fd = sockfd;
                ev.events = EPOLLIN | EPOLLET;
                epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);

            }

        }
    }

    return 0;
}
